# -*- coding: UTF-8 -*-
import paramiko
from Code.Utils.sc_func import Common
from Code.Model.sc_global import Global
from Code.Model.Data.sc_data import model_gate


class SSH(object):
    """ paramiko ssh登录服务类 """

    def __init__(self, ip, port, user, upwd, rpwd):
        self._host_ip = ip
        self._port = port
        self._user_name = user
        self._user_pwd = upwd
        self._root_pwd = rpwd
        self._ssh_client = None
        self._ftp_client = None

    def execute(self, cmd, root=False):
        """ 命令执行实现
        普通用户和root用户统一使用英文回显
        """
        cmd = '''export LC_ALL=C &>/dev/null; %s''' % cmd
        if root:
            cmd = '''export LC_ALL=C &>/dev/null; echo "%s"|su - -c "%s"''' % (self._root_pwd, cmd)
        if not self._ssh_client:
            self._ssh_client = paramiko.SSHClient()
            self._ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
            self._ssh_client.connect(self._host_ip, port=self._port, username=self._user_name, password=self._user_pwd)
        return self._ssh_client.exec_command(cmd)    # stdin, stdout, stderr

    def upload(self, local_path, server_path, callback=None):
        if not self._ftp_client:
            self._ftp_client = paramiko.Transport((self._host_ip, self._port))
            self._ftp_client.connect(username=self._user_name, password=self._user_pwd)
        with paramiko.SFTPClient.from_transport(self._ftp_client) as ftp:
            ftp.put(local_path, server_path, callback=callback)

    def download(self, server_path, local_path, callback=None):
        if not self._ftp_client:
            self._ftp_client = paramiko.Transport((self._host_ip, self._port))
            self._ftp_client.connect(username=self._user_name, password=self._user_pwd)
        with paramiko.SFTPClient.from_transport(self._ftp_client) as ftp:
            ftp.get(server_path, local_path, callback=callback)

    def close(self):
        if self._ssh_client:
            self._ssh_client.close()
            self._ssh_client = None
        if self._ftp_client:
            self._ftp_client.close()
            self._ftp_client = None


class SSHUtil:
    """ SSH公共方式类 """
    @classmethod
    def execute(cls, ssh_inst, cmd, root):
        try:
            _, out, err = ssh_inst.execute(cmd, root)
            ret = False if out.channel.recv_exit_status() else True
            out = str(out.read().strip(), encoding='UTF-8', errors='ignore')
            err = err.read()
            return ret, out, err
        except Exception as e:
            return False, None, e

    @classmethod
    def exec_info(cls, ssh_inst, cmd, root=False):
        ret, out, err = cls.execute(ssh_inst, cmd, root)
        if ret:
            err = None
        return out, err

    @classmethod
    def exec_ret(cls, ssh_inst, cmd, root=False):
        ret, _, err = cls.execute(ssh_inst, cmd, root)
        return ret, err

    @classmethod
    def user_login(cls, ssh_inst, user_name):
        root = True if user_name == 'root' else False
        ret, err = cls.exec_ret(ssh_inst, 'whoami|grep %s' % user_name, root)
        if ret:
            return True, None
        return False, err

    @classmethod
    def upload_file(cls, ssh_inst, local, remote, callback=None):
        try:
            ssh_inst.upload(local, remote, callback)
        except Exception as e:
            return False, e
        return True, None

    @classmethod
    def download_file(cls, ssh_inst, remote, local, callback=None):
        try:
            ssh_inst.download(remote, local, callback)
        except Exception as e:
            return False, e
        return True, None

    @classmethod
    def upload_file_root(cls, ssh_inst, local_file, remote_dir, callback=None):
        """ 先上传到__UPLOAD__目录, 再root移动到目标目录, 避开普通用户可能权限不足问题 """
        if not Common.is_file(local_file):
            return False, '{} no such file.'.format(local_file)
        file_name = Common.trans_char(Common.basename(local_file))
        server_upload = Global.G_SERVER_UPLOAD % model_gate.settings_data.server_dir
        temp_name = '__UPLOAD_FILE_{}_{}'.format(str(Common.time()).replace('.', ''), file_name)
        temp_upload = '{}/{}'.format(server_upload, temp_name)
        cls.upload_file(ssh_inst, local_file, temp_upload, callback)
        server_name = '{}/{}'.format(remote_dir, file_name)
        cmd = 'mv -f {0} {1} && chmod 777 {1}'.format(temp_upload, server_name)
        return cls.exec_ret(ssh_inst, cmd, True)

    @classmethod
    def upload_dir_root(cls, ssh_inst, local, remote, callback=None):
        if not Common.is_dir(local):
            return False, '{} no such directory.'.format(local)
        local_dir_name = Common.basename(local)
        remote = '{}/{}'.format(remote, local_dir_name)
        zip_name = '__UPLOAD_DIR_{}__.tar.gz'.format(str(Common.time()).replace('.', ''))
        zip_path = '{}/{}'.format(Common.dirname(local), zip_name)
        # 打包 #
        Common.zip_dir(local, zip_path)

        # 使用root用户先创建一下目标目录 #
        cls.exec_ret(ssh_inst, 'mkdir -p {0}; chmod 777 {0}'.format(remote), True)
        server_upload = Global.G_SERVER_UPLOAD % model_gate.settings_data.server_dir
        upload_path = '{}/{}'.format(server_upload, zip_name)
        dest_path = '{}/{}'.format(remote, zip_name)

        # 普通用户上传压缩包 #
        ret, err = cls.upload_file(ssh_inst, zip_path, upload_path, callback)
        Common.remove(zip_path)
        if not ret:
            return ret, err
        # 使用root用户移动压缩包至目标目录并修改权限 #
        ret, err = cls.exec_ret(ssh_inst, 'mv {0} {1} && chmod 777 {1}'.format(upload_path, dest_path), True)
        if not ret:
            return ret, err
        # 使用普通用户解压缩并删除压缩包 #
        return cls.exec_ret(ssh_inst, 'cd {0} && tar zxf {1} && rm {1}'.format(remote, zip_name))

    @classmethod
    def download_dir_root(cls, ssh_inst, remote_list, local, callback):
        server_download = Global.G_SERVER_DOWNLOAD % model_gate.settings_data.server_dir
        temp_name = '__DOWNLOAD_DIR_{}__'.format(str(Common.time()).replace('.', ''))
        temp_dir = '{}/{}'.format(server_download, temp_name)
        cmd = 'mkdir -p {0} && chmod 777 {0}'.format(temp_dir)
        for remote in remote_list:
            cmd = '{} && cp -rf {} {}/'.format(cmd, remote, temp_dir)
        cmd = '{0} && cd {1} && tar zcf {2}.tar.gz * && chmod 777 {2}.tar.gz'.format(cmd, temp_dir, temp_name)
        ret, err = cls.exec_ret(ssh_inst, cmd, True)
        if not ret:
            return ret, err
        temp_download = '{}/{}.tar.gz'.format(temp_dir, temp_name)
        temp_local = '{}/{}.tar.gz'.format(local, temp_name)
        ret, err = cls.download_file(ssh_inst, temp_download, temp_local, callback)
        if not ret:
            return ret, err
        ret = Common.unzip_file(temp_local, local)
        if not ret:
            return False, 'unzip failed.'
        Common.remove(temp_local)
        return True, None

